// Copyright 2017 Baidu Inc. All Rights Reserved.
// Author: tai xiurong (taixiurong@baidu.com)
//
// Description: duer log report to server

#include "baidu_duer_log_report.h"
#include "baidu_ca_scheduler.h"
#include "duer_log.h"
#include "heap_monitor.h"

// the longest report info, if longer than this, will be truncated
const static int s_max_report_data_len = 256;
void duer_log_report(const char* tag, const char* format, ...) {
    bool report = duer::DuerLogReport::instance()->is_report_log(tag);
    if(!report){
        return;
    }

    char buffer[s_max_report_data_len] = {0};
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, s_max_report_data_len, format, arg);
    va_end(arg);
    int ret = duer::DuerLogReport::instance()->report_log_info(tag, buffer);
}

namespace duer {

DuerLogReport::DuerLogReport() :
        _report_level(LOG_W)
#ifdef DUROG_REPORT_QUEUE_ENABLE
        ,_max_log_arr_max(50)
        ,_current_log_obj_index(0)
        ,_log_json_string_arr(NULL)
#endif
{
}

DuerLogReport::~DuerLogReport() {
#ifdef DUROG_REPORT_QUEUE_ENABLE
    if(_log_json_string_arr != NULL){
        delete [] _log_json_string_arr;
    }
#endif
}

DuerLogReport* DuerLogReport::instance() {
    static DuerLogReport _s_instance;
    return &_s_instance;
}

bool DuerLogReport::is_report_log(const char* tag){
    int level = get_level_by_tag(tag);
    if(LOG_V == level){
        return false;
    }
    if (level > _report_level) {
        return false;
    }
    return true;
}

int DuerLogReport::report_log_format(const char* tag, const char* format, ...) {
    if (NULL == tag || NULL == format) {
        return -1;
    }
    char buffer[s_max_report_data_len] = {0};
    va_list arg;
    va_start(arg, format);
    vsnprintf(buffer, s_max_report_data_len, format, arg);
    va_end(arg);
    int ret = to_json_and_send(tag, buffer);
    return ret;
}

int DuerLogReport::report_log_info(const char* tag, const char* info) {
    if (NULL == tag || NULL == info) {
        return -1;
    }
    int ret = to_json_and_send(tag, info);
    return ret;
}

int DuerLogReport::to_json_and_send(const char* tag, const char* info) {
    Object value;
    value.putString("tag", tag);
    value.putString("info", info);
    Object log_info;
    log_info.putObject("sys_log_info", value);
    int ret = 0;

#ifndef DUROG_REPORT_QUEUE_ENABLE
    ret = send(&log_info);
    if (ret < 0) {
        printf("log_info report failed!!! error log info = %s\n", log_info.toString());
    }
    return ret;

#else
    _report_obj_mutex.lock();

    if(NULL == _log_json_string_arr){

        _log_json_string_arr = NEW(OS) ReportData [_max_log_arr_max];

        if(NULL == _log_json_string_arr){
            ret = send(&log_info);
             _report_obj_mutex.unlock();
            return ret;
        }
    }

    const char* json_str = log_info.toString();
    int len = strlen(json_str);
    _log_json_string_arr[_current_log_obj_index].len = len;
    _log_json_string_arr[_current_log_obj_index].info = (char*)MALLOC(len,OS);

    if(NULL == _log_json_string_arr[_current_log_obj_index].info){
        ret = send(&log_info);
        _report_obj_mutex.unlock();
        return ret;
    }

    memmove(_log_json_string_arr[_current_log_obj_index].info,json_str,len);
    _current_log_obj_index++;

    if (_current_log_obj_index >= _max_log_arr_max) {

        for (int i = 0; i < _current_log_obj_index; ++i) {

            ret = send(_log_json_string_arr[i].info,_log_json_string_arr[i].len);

            if (ret < 0) {
                printf("log_info report failed!!! error log info = %s\n", _log_json_string_arr[i].info);
            }

            //printf("report log_info log info = %s\n", _log_json_string_arr[i].info);

            FREE(_log_json_string_arr[i].info);

            _log_json_string_arr[i].info = NULL;
            _log_json_string_arr[i].len = 0;
        }
        _current_log_obj_index = 0;
    }

    _report_obj_mutex.unlock();

    return ret;
#endif

}

void DuerLogReport::set_report_level(LogReportLevel level) {
    if (level > LOG_W) { // report level at least is LOG_W
        return;
    }
    _report_level = level;
}

int DuerLogReport::get_level_by_tag(const char* tag) {
    int ret = LOG_MAX;
    if (0 == strcmp(tag, "V")) {
        ret = LOG_V;
    } else if (0 == strcmp(tag, "D")) {
        ret = LOG_D;
    } else if (0 == strcmp(tag, "I")) {
        ret = LOG_I;
    } else if (0 == strcmp(tag, "W")) {
        ret = LOG_W;
    } else if (0 == strcmp(tag, "E")) {
        ret = LOG_E;
    } else {
        // keep ret is LOG_MAX
    }
    return ret;
}
int DuerLogReport::send(const Object* obj) {
    int rs = Scheduler::instance().report(*obj);
    return rs;
}

int DuerLogReport::send(const void* data, size_t size){
    int rs = Scheduler::instance().report(data, size);
    return rs;
}

}//namespace duer
